﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.IO;
using System.Threading.Tasks;

namespace Development.Tools
{
    /// <summary>
    /// log level
    /// </summary>
    public enum ELogLevel
    {
        LOG_INFO,
        LOG_DEBUG,
        LOG_ERROR,
        LOG_FATAL,
        LOG_UNKNOWN
    }

    /// <summary>
    /// log class ,it can out put log in consle and file
    /// </summary>
    public class Log : Singleton<Log>,IDisposable
    {
        /// <summary>
        /// lock obj
        /// </summary>
        private object m_lock = new object();

        /// <summary>
        /// log queue
        /// </summary>
        private Queue<string> m_logQueue = new Queue<string>();

        /// <summary>
        /// is thread run
        /// </summary>
        private bool m_Run = false;


        /// <summary>
        /// thread obj
        /// </summary>
        private Task m_thread = null;


        private string m_path;


        private DateTime m_lastDate;

        private StreamWriter m_streamWriter = null;

        /// <summary>
        /// start log thread
        /// </summary>
        public void StartLog(string ServerName,string path)
        {
            if (!Directory.Exists(path))
            {
                Directory.CreateDirectory(path);
            }

            if (!Directory.Exists(path + ServerName))
            {
                Directory.CreateDirectory(path + ServerName);
            }
            m_lastDate = DateTime.Now;
            m_path = path + ServerName + "/";
            CreateFile(GetDate());
 
            try
            {
                m_Run = true;
                m_thread = new Task(OnRun);
                m_thread.Start();
              //  GenerateLog(ELogLevel.LOG_INFO, "log thread start success!");
               
            }
            catch (Exception e)
            {
                GenerateLog(ELogLevel.LOG_ERROR, e.Message);
            }
 
            
        }

        protected void CreateFile(string date)
        {
            lock (m_lock)
            {
                if (m_streamWriter != null)
                {
                    m_streamWriter.Flush();
                    m_streamWriter.Close();
                }
                try
                {
                    m_streamWriter = new StreamWriter(m_path + date + ".txt", true);
                }
                catch (Exception e)
                {
                    WriteLog(ELogLevel.LOG_ERROR, "write log error:" + e.Message); 
                }
            }
        }


        public void Dispose()
        {
            StopLog();
        }
        /// <summary>
        /// stop log thread
        /// </summary>
        public void StopLog()
        {
            m_Run = false;

            if (m_thread != null)
            {
                m_thread = null;
            }
            lock (m_lock)
            {

                if (m_streamWriter != null)
                {
                    m_streamWriter.Close();
                    m_streamWriter = null;
                }
            }


        }

        /// <summary>
        /// write log 
        /// </summary>
        public static void WriteLog(ELogLevel level, string content)
        {
            Log.Instance.GenerateLog(level, content);
        }

        public static void WriteLog(ELogLevel level,Exception e)
        {
            Log.Instance.GenerateLog(level, string.Format("{0};info[instance:{1}methed:{2}stack:{3}", e.Message, e.Source, e.TargetSite.Name, e.StackTrace));
        }

        /// <summary>
        /// generate log
        /// </summary>
        private void GenerateLog(ELogLevel level, string content)
        {
            string errorFlag = "[UNKNOWN]";
            string log = "";
            switch (level)
            {
                case ELogLevel.LOG_INFO: errorFlag = "[INFO]"; break;
                case ELogLevel.LOG_DEBUG: errorFlag = "[DEBUG]"; break;
                case ELogLevel.LOG_ERROR: errorFlag = "[ERROR]"; break;
                case ELogLevel.LOG_FATAL: errorFlag = "[FATAL]"; break;
                case ELogLevel.LOG_UNKNOWN: errorFlag = "[UNKNOWN]"; break;
                default: break;
            }

#if DEBUG

            log = string.Format("[{0}] {1} {2}    {3}",GetTime(), errorFlag, content, GetCodeFileInfo());
#else
            log = string.Format("{[0}] {1} {2}", GetTime(),errorFlag, content);
#endif
            if (m_Run)
            {
                Enqueue(log);
            }
            else
            {
                Console.WriteLine(log);
            }


        }

        /// <summary>
        /// get code info string
        /// </summary>
        public string GetCodeFileInfo()
        {

            StackTrace stackTrace = new StackTrace(true);
            StackFrame frame = stackTrace.GetFrame(3);
            string fileName = frame.GetFileName();
            string[] names = fileName.Split('\\');
            fileName = names[names.Length - 1];
            return fileName + " " + frame.GetFileLineNumber();

        }

        public string GetDate()
        {
            string date = DateTime.Now.Date.ToShortDateString();
            date = date.Replace('/', '-');
            return date;
        }

        public string GetTime()
        {
            DateTime now = DateTime.Now;
            if (m_Run)
            {
                if (now.Day != m_lastDate.Day)
                {
                    CreateFile(GetDate());
                }
            }

            return now.ToLongTimeString();
        }
        /// <summary>
        /// enter log in equeue
        /// </summary>
        private void Enqueue(string log)
        {
            lock (m_lock)
            {
                m_logQueue.Enqueue(log);
            }
        }

        /// <summary>
        /// get log from equeue
        /// </summary>
        private string Dequeue()
        {
            lock (m_lock)
            {
                if (m_logQueue.Count > 0)
                {
                    return m_logQueue.Dequeue();
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// work thread
        /// </summary>
        protected void OnRun()
        {
            while (m_Run)
            {
                string log = Dequeue();
                if (!string.IsNullOrEmpty(log))
                {
                    OutputLog(log);
                }
                else
                {
                    Thread.Sleep(20);
                }
            }
        }

        /// <summary>
        /// out put log
        /// </summary>
        private void OutputLog(string log)
        {
            //output in consle
            if (log.Contains("[INFO]"))
            {
                Console.WriteLine(log);
            }
            else if(log.Contains("[DEBUG]"))
            {
                Console.ForegroundColor = ConsoleColor.DarkGreen;
                Console.WriteLine(log);
                Console.ResetColor();
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.DarkRed;
                Console.WriteLine(log);
                Console.ResetColor();
            }
     

            lock (m_lock)
            {
                //output in file
                if (m_streamWriter != null)
                {
                    m_streamWriter.WriteLine(log);
                    m_streamWriter.Flush();
                }
            }
        }

    }
}
